package com.lyeducation.security;

import com.lyeducation.core.base.SysConstants;
import com.lyeducation.security.config.*;
import com.lyeducation.util.jwt.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  /** 注入url资源检验过滤器，返回该url所拥有的权限 */
  @Autowired UrlFilterInvocationSecurityMetadataSource urlFilterInvocationSecurityMetadataSource;
  /** 注入url资源权限匹配管理器，检验当前url所在权限是否属于当前登录者所拥有的权限 */
  @Autowired UrlAccessDecisionManager urlAccessDecisionManager;
  /** 注入权限不足返回信息类 */
  @Autowired AuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;
  // @Autowired SecurityUsernamePasswordAuthenticationFilter
  // securityUsernamePasswordAuthenticationFilter;
  @Value("#{'${ips}'.split(',')}")
  private List<String> iplist;

  /** 注入登录成功失败Hander */
  @Resource(name = "authenticationSuccessHandlerImpl")
  private AuthenticationSuccessHandler authenticationSuccessHandlerImpl;

  @Autowired private AuthenticationFailureHandlerImpl authenticationFailureHandlerImpl;
  @Autowired private JwtUtil jwtUtil;
  /** 自定义 登录 过滤器 */
  @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

  @Resource(name = "sysUserServiceImpl")
  private UserDetailsService sysUserServiceImpl;

  @Autowired private SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;

  @Autowired private SecurityAccessDeniedHandler securityAccessDeniedHandler;

  @Autowired
  private SpringSocialConfigurer merryyouSpringSocialConfigurer;

  public static void setAuthenticationToSecurityContext(
      UserDetails userDetails, HttpServletRequest request) {
    if (userDetails != null) {
      UsernamePasswordAuthenticationToken authentication =
          new UsernamePasswordAuthenticationToken(
              userDetails, userDetails.getPassword(), userDetails.getAuthorities());
      authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
      SecurityContextHolder.getContext().setAuthentication(authentication);
    }
  }

  /**
   * 注入登陆服务，请求login url时会执行该服务的loadUserByUsername()
   *
   * @param auth
   * @throws Exception
   */
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(sysUserServiceImpl).passwordEncoder(new BCryptPasswordEncoder());
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    String[] strings = new String[iplist.size()];
    // .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
    //    //    // 允许对于网站静态资源的无授权访问
    //    //            .antMatchers(
    //    //            "/", "/*.html", "/favicon.ico", "/**/*.html", "/**/*.css", "/**/*.js")
    //    //            .permitAll()
      //    不拦截异常抛出接口使其不会被filter
      web.ignoring()
              .antMatchers(iplist.toArray(strings)).antMatchers("/error")
        .antMatchers("/index.html", "/static/**", "/sysUser/**")
            .antMatchers(SysConstants.PROJECT_SWAGGER_UI_HTML_URL.getValue())
        .antMatchers("/webjars/**")
        .antMatchers("/v2/**")
              .antMatchers("/swagger-resources/**")
              .antMatchers(
                      //                    SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                      //            这里是全局过滤请求，会直接跳过security的filter链所以如果设置了
                      // SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM则不会执行对应的
                      // SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM filter
            SecurityConstants.DEFAULT_SOCIAL_PROCESS_URL,
//                      添加   SecurityConstants.DEFAULT_SOCIAL_PROCESS_URL + "/**"导致没有走security的流程直接通过至controller
//                      SecurityConstants.DEFAULT_SOCIAL_PROCESS_URL + "/**",
                      SecurityConstants.DEFAULT_REGISTER_URL,
                      SecurityConstants.DEFAULT_SIGNUP_URL,
                      SecurityConstants.VIDEONOTROLEURL,
                      SecurityConstants.FAVICON_ICO, SecurityConstants.OSSCALLBACK, SecurityConstants.STATICRESOURCES,
                      "/sysTest/**",
                      "/social/info",
                      "/**/*.js",
            "/**/*.css",
            "/**/*.jpg",
                      "/**/*.jpeg", "/**/*.txt",
            "/**/*.png",
            "/**/*.woff2",
                      "/code/*");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    //        防止关于HTTP头部信息X-Frame-Options的问题-防止网站被人嵌套
      //        https://blog.csdn.net/lyj1101066558/article/details/51320246
      http.authorizeRequests().antMatchers("/index.html", "/static/**").permitAll();
      http.headers()
              .frameOptions()
              .disable()
              .and()
              .formLogin()
        // 定义loginPage url为login_p抛出异常最终都会走login_p服务 "/login_p"
              //            全部在自定义    jwtTokenAuthenticationFilter 替换
              // UsernamePasswordAuthenticationFilter设置
            .loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
        .loginProcessingUrl(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM)
        .usernameParameter("username")
            .passwordParameter("password")
        .failureHandler(authenticationFailureHandlerImpl)
        .successHandler(authenticationSuccessHandlerImpl)
            .and()
              .apply(merryyouSpringSocialConfigurer) // 社交登录
            .and()
            .rememberMe()
            .and()
              .logout()
              .deleteCookies("JSESSIONID")
        .permitAll()
              .and()
              .authorizeRequests()
            //            安全模块加入url资源过滤器url权限过滤器
            .withObjectPostProcessor(
                    new ObjectPostProcessor<FilterSecurityInterceptor>() {
                      @Override
                      public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setSecurityMetadataSource(urlFilterInvocationSecurityMetadataSource);
                        o.setAccessDecisionManager(urlAccessDecisionManager);
                        return o;
                      }
                    })

        //        .and().authorizeRequests()
        //            // 所有/trace/user/ 的所有请求 都放行
        //            .antMatchers("/trace/users/**").permitAll()
        //            // swagger start
        //            .antMatchers("/swagger-ui.html").permitAll()
        //            .antMatchers("/swagger-resources/**").permitAll()
        //            .antMatchers("/images/**").permitAll()
        //            .antMatchers("/webjars/**").permitAll()
        //            .antMatchers("/v2/**").permitAll()
        //            .antMatchers("/configuration/ui").permitAll()
        //            .antMatchers("/configuration/security").permitAll()
        //            // 除上面外的所有请求全部需要鉴权认证
        //            .anyRequest()
        //            .authenticated()
        //            // swagger end
        .and()
        // 由于使用的是JWT，我们这里不需要csrf
        .csrf()
        .disable()
        // 基于token，所以不需要session
        // 如果没有设置，则当jwttoken验证不通过例如jwttoken过期了，
        // 由于session还会保存账号信息导致后面依然能获取当前线程session对应登录者的权限，从而使得请求的url资源依然能够通过
        // 设置不需要session则每次請求都无法从session中获取到登录者权限信息，
        // 通过jwttoken验证，通过了便设置一次本次请求的一个临时session，供后面的url资源需要的权限匹配验证
        //            完事后应该是清空该session，使得下次的请求无法从session获得权限，
        // 需要jwttoken通过后从新设置一遍当前的权限

        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
        .exceptionHandling()
        .accessDeniedHandler(authenticationAccessDeniedHandler);

    //        // 对于获取token的rest api要允许匿名访问
    //        .antMatcher("/login/**")
    //        .permitAll()

    // 添加 filter
    http.addFilterAt(jwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    http.addFilterBefore(jwtAuthenticationTokenFilter, AuthenticationProcessingFilter.class);

    // 禁用缓存
    http.headers().cacheControl();
    http.exceptionHandling()
        .authenticationEntryPoint(securityAuthenticationEntryPoint)
        .accessDeniedHandler(securityAccessDeniedHandler);
  }

  /** 自定义登录过滤器 */
  private AuthenticationProcessingFilter jwtTokenAuthenticationFilter() throws Exception {
    AuthenticationProcessingFilter authenticationProcessingFilter =
            new AuthenticationProcessingFilter(SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_FORM);
    authenticationProcessingFilter.setJwtUtil(jwtUtil);
    // SimpleUrlAuthenticationSuccessHandler successHandler = new
    // SimpleUrlAuthenticationSuccessHandler();
    // successHandler.setAlwaysUseDefaultTargetUrl(true);
    // successHandler.setDefaultTargetUrl("/user");

    authenticationProcessingFilter.setAuthenticationManager(this.authenticationManager());
    authenticationProcessingFilter.setAuthenticationSuccessHandler(authenticationSuccessHandlerImpl);
    authenticationProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandlerImpl);
    return authenticationProcessingFilter;
  }
}
